package com.eedi.framework.permission.service;

import cn.hutool.core.collection.CollUtil;
import com.google.common.annotations.VisibleForTesting;
import com.eedi.framework.common.enums.YesOrNoEnum;
import com.eedi.framework.common.util.collection.CollectionUtils;
import com.eedi.framework.common.util.object.BeanUtils;
import com.eedi.framework.permission.controller.org.vo.menu.OrgMenuListReq;
import com.eedi.framework.permission.controller.org.vo.menu.OrgMenuResp;
import com.eedi.framework.permission.controller.org.vo.menu.OrgMenuSaveReq;
import com.eedi.framework.permission.convert.OrgMenuConvert;
import com.eedi.framework.permission.dal.OrgPermissionCacheKeyConstants;
import com.eedi.framework.permission.dal.dataobject.OrgMenuDO;
import com.eedi.framework.permission.dal.mapper.OrgMenuMapper;
import com.eedi.framework.permission.enums.OrgMenuTypeEnum;
import com.eedi.framework.permission.enums.OrgPermissionErrorCodeConstants;
import com.eedi.framework.template.dal.dataobject.SysTemplateMenuDO;
import com.eedi.framework.template.service.SysTemplateMenuService;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

import static com.eedi.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.eedi.framework.common.util.collection.CollectionUtils.convertList;
import static com.eedi.framework.permission.enums.OrgPermissionErrorCodeConstants.ORG_TEMPLATE_MENU_IS_NULL;

/**
 * 菜单 Service 实现
 *
 * @author 永聚长青源码
 */
@Service
@Slf4j
public class OrgMenuServiceImpl implements OrgMenuService {

    @Resource
    private OrgMenuMapper orgMenuMapper;
    @Resource
    private SysTemplateMenuService sysTemplateMenuService;


    @Override
    @CacheEvict(value = OrgPermissionCacheKeyConstants.PERMISSION_MENU_ID_LIST, key = "#createReqVO.orgMenuPermission",
            condition = "#createReqVO.orgMenuPermission != null")
    public String createMenu(OrgMenuSaveReq createReqVO) {
        // 校验父菜单存在
        validateParentMenu(createReqVO.getParentOrgMenuId(), null);
        // 校验菜单（自己）
        validateMenu(createReqVO.getParentOrgMenuId(), createReqVO.getOrgMenuName(), null);

        // 插入数据库
        OrgMenuDO menu = BeanUtils.toBean(createReqVO, OrgMenuDO.class);
        initMenuProperty(menu);
        orgMenuMapper.insert(menu);
        // 返回
        return menu.getOrgMenuId();
    }

    @Override
    @CacheEvict(value = OrgPermissionCacheKeyConstants.PERMISSION_MENU_ID_LIST,
            allEntries = true) // allEntries 清空所有缓存，因为 permission 如果变更，涉及到新老两个 permission。直接清理，简单有效
    public void updateMenu(OrgMenuSaveReq updateReqVO) {
        // 校验更新的菜单是否存在
        if (orgMenuMapper.selectById(updateReqVO.getOrgMenuId()) == null) {
            throw exception(OrgPermissionErrorCodeConstants.ORG_MENU_NOT_EXISTS);
        }
        // 校验父菜单存在
        validateParentMenu(updateReqVO.getParentOrgMenuId(), updateReqVO.getOrgMenuId());
        // 校验菜单（自己）
        validateMenu(updateReqVO.getParentOrgMenuId(), updateReqVO.getOrgMenuName(), updateReqVO.getOrgMenuId());

        // 更新到数据库
        OrgMenuDO updateObj = BeanUtils.toBean(updateReqVO, OrgMenuDO.class);
        initMenuProperty(updateObj);
        orgMenuMapper.updateById(updateObj);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = OrgPermissionCacheKeyConstants.PERMISSION_MENU_ID_LIST,
            allEntries = true) // allEntries 清空所有缓存，因为此时不知道 id 对应的 permission 是多少。直接清理，简单有效
    public void deleteMenu(String menuId) {
        // 校验是否还有子菜单
        if (orgMenuMapper.selectCountByParentId(menuId) > 0) {
            throw exception(OrgPermissionErrorCodeConstants.ORG_MENU_EXISTS_CHILDREN);
        }
        // 校验删除的菜单是否存在
        if (orgMenuMapper.selectById(menuId) == null) {
            throw exception(OrgPermissionErrorCodeConstants.ORG_MENU_NOT_EXISTS);
        }
        // 标记删除
        orgMenuMapper.deleteById(menuId);
    }

    @Override
    public List<OrgMenuDO> getMenuList() {
        return orgMenuMapper.selectList();
    }

    @Override
    public List<OrgMenuDO> getMenuListByTenant(OrgMenuListReq reqVO) {
        List<OrgMenuDO> menus = getMenuList(reqVO);
        // 开启多租户的情况下，需要过滤掉未开通的菜单
//        tenantService.handleTenantMenu(menuIds -> menus.removeIf(menu -> !CollUtil.contains(menuIds, menu.getId())));
        return menus;
    }

    @Override
    public List<OrgMenuDO> getMenuList(OrgMenuListReq reqVO) {
        return orgMenuMapper.selectList(reqVO);
    }

    @Override
    @Cacheable(value = OrgPermissionCacheKeyConstants.PERMISSION_MENU_ID_LIST, key = "#permission")
    public List<String> getMenuIdListByPermissionFromCache(String permission) {
        List<OrgMenuDO> menus = orgMenuMapper.selectListByPermission(permission);
        return convertList(menus, OrgMenuDO::getOrgMenuId);
    }

    @Override
    public OrgMenuDO getMenu(String menuId) {
        return orgMenuMapper.selectById(menuId);
    }

    @Override
    public List<OrgMenuDO> getMenuList(Collection<String> menuIds) {
        if(CollectionUtils.isAnyEmpty(menuIds)){
            return Collections.emptyList();
        }
        return orgMenuMapper.selectBatchIds(menuIds);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String,String> initMenuListByOrgInfoId(String orgInfoId) {
        Map<String,String> keyMap = new HashMap<>();
        //读取菜单模版中的数据
        List<SysTemplateMenuDO> menuList = sysTemplateMenuService.getMenuList();
        //转换数据
        List<OrgMenuResp> metaOrgMenus = OrgMenuConvert.INSTANCE.convert(menuList);
        if (CollUtil.isEmpty(metaOrgMenus)){
            throw exception(ORG_TEMPLATE_MENU_IS_NULL);
        }
        //转换为树形
        List<OrgMenuResp> orgMenuResps = OrgMenuConvert.INSTANCE.buildMenuTree(metaOrgMenus);
        //循环插入菜单信息
        saveChildren(orgInfoId,OrgMenuDO.ORG_ID_ROOT,orgMenuResps,keyMap);
        return keyMap;
    }

    private void saveChildren(String orgInfoId,String parentId,List<OrgMenuResp> children,Map<String,String> menuContainer){
        children.forEach(menu->{
            menu.setParentOrgMenuId(parentId);
            OrgMenuDO orgMenuDO = OrgMenuConvert.INSTANCE.convertDO(menu);
            //清空id，使用自动生成的
            orgMenuDO.setOrgMenuId(null);
            orgMenuDO.setOrgInfoId(orgInfoId);
            orgMenuMapper.insert(orgMenuDO);
            menuContainer.put(menu.getOrgMenuId(),orgMenuDO.getOrgMenuId());
            if (CollUtil.isNotEmpty(menu.getChildren())){
                saveChildren(orgInfoId,orgMenuDO.getOrgMenuId(),menu.getChildren(),menuContainer);
            }
        });
    }

    /**
     * 校验父菜单是否合法
     * <p>
     * 1. 不能设置自己为父菜单
     * 2. 父菜单不存在
     * 3. 父菜单必须是 {@link OrgMenuTypeEnum#MENU} 菜单类型
     *
     * @param parentId 父菜单编号
     * @param childId  当前菜单编号
     */
    @VisibleForTesting
    void validateParentMenu(String parentId, String childId) {
        if (parentId == null || OrgMenuDO.ORG_ID_ROOT.equals(parentId)) {
            return;
        }
        // 不能设置自己为父菜单
        if (parentId.equals(childId)) {
            throw exception(OrgPermissionErrorCodeConstants.ORG_MENU_PARENT_ERROR);
        }
        OrgMenuDO menu = orgMenuMapper.selectById(parentId);
        // 父菜单不存在
        if (menu == null) {
            throw exception(OrgPermissionErrorCodeConstants.ORG_MENU_PARENT_NOT_EXISTS);
        }
        // 父菜单必须是目录或者菜单类型
        if (!OrgMenuTypeEnum.DIR.equals(menu.getOrgMenuType())
                && !OrgMenuTypeEnum.MENU.equals(menu.getOrgMenuType())) {
            throw exception(OrgPermissionErrorCodeConstants.ORG_MENU_PARENT_NOT_DIR_OR_MENU);
        }
    }

    /**
     * 校验菜单是否合法
     * <p>
     * 1. 校验相同父菜单编号下，是否存在相同的菜单名
     *
     * @param name     菜单名字
     * @param parentId 父菜单编号
     * @param menuId       菜单编号
     */
    @VisibleForTesting
    void validateMenu(String parentId, String name, String menuId) {
        OrgMenuDO menu = orgMenuMapper.selectByParentIdAndName(parentId, name);
        if (menu == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的菜单
        if (StringUtil.isEmpty(menuId)) {
            throw exception(OrgPermissionErrorCodeConstants.ORG_MENU_NAME_DUPLICATE);
        }
        if (!menu.getOrgMenuId().equals(menuId)) {
            throw exception(OrgPermissionErrorCodeConstants.ORG_MENU_NAME_DUPLICATE);
        }
    }

    /**
     * 初始化菜单的通用属性。
     * <p>
     * 例如说，只有目录或者菜单类型的菜单，才设置 icon
     *
     * @param menu 菜单
     */
    private void initMenuProperty(OrgMenuDO menu) {
        // 菜单为按钮类型时，无需 component、icon、path 属性，进行置空
        if (OrgMenuTypeEnum.BUTTON.equals(menu.getOrgMenuType())) {
            menu.setOrgMenuComponent("");
            menu.setOrgMenuComponentName("");
            menu.setOrgMenuIcon("");
            menu.setOrgMenuPath("");
        }
        menu.setOrgMenuVisible(YesOrNoEnum.YES);
        menu.setOrgMenuKeepAlive(YesOrNoEnum.YES);
        menu.setOrgMenuAlwaysShow(YesOrNoEnum.YES);
    }
    /**
     * 通过机构ID构建主菜单,并插入数据库 TODO
     * @param orgInfoId 机构ID
     * @return
     */
    private List<OrgMenuDO> initSuperParentMenuByOrgInfoId(String orgInfoId){
        OrgMenuDO orgMenu = new OrgMenuDO();
        orgMenu.setOrgMenuType(OrgMenuTypeEnum.DIR);
        initMenuProperty(orgMenu);
        orgMenu.setOrgMenuName("系统管理");
        orgMenu.setOrgInfoId(orgInfoId);
        orgMenu.setParentOrgMenuId(OrgMenuDO.ORG_ID_ROOT);
        orgMenu.setOrgMenuPermission("");
        orgMenu.setOrgMenuSort(10);
        this.orgMenuMapper.insert(orgMenu);
        return  List.of(orgMenu);
    }

//    /**
//     * 通过机构ID构建主菜单,并插入数据库 TODO
//     * @param orgInfoId 机构ID
//     * @return
//     */
//    private List<OrgMenuDO> initSuperParentMenuByOrgInfoId(String orgInfoId){
//        OrgMenuDO orgMenu = new OrgMenuDO();
//        orgMenu.setOrgMenuType(OrgMenuTypeEnum.DIR);
//        initMenuProperty(orgMenu);
//        orgMenu.setOrgMenuName("系统管理");
//        orgMenu.setOrgInfoId(orgInfoId);
//        orgMenu.setParentOrgMenuId(OrgMenuDO.ORG_ID_ROOT);
//        orgMenu.setOrgMenuPermission("");
//        orgMenu.setOrgMenuSort(10);
//        this.orgMenuMapper.insert(orgMenu);
//        return  List.of(orgMenu);
//    }

    /**
     * 通过父菜单以及机构ID来构建子菜单,并插入数据库 TODO
     * @param superMenu 父菜单
     * @param orgInfoId 机构ID
     * @return 子菜单列表
     */
    private List<OrgMenuDO> initSubMenus(OrgMenuDO superMenu, String orgInfoId) {

        OrgMenuDO orgMenuOrgInfo = new OrgMenuDO();
        orgMenuOrgInfo.setOrgMenuType(OrgMenuTypeEnum.MENU);
        initMenuProperty(orgMenuOrgInfo);
        orgMenuOrgInfo.setParentOrgMenuId(superMenu.getOrgMenuId());
        orgMenuOrgInfo.setOrgMenuName("机构管理");
        orgMenuOrgInfo.setOrgInfoId(orgInfoId);
        orgMenuOrgInfo.setOrgMenuPermission("org:info:list");
        orgMenuOrgInfo.setOrgMenuSort(20);
        this.orgMenuMapper.insert(orgMenuOrgInfo);

        OrgMenuDO orgMenuDetp = new OrgMenuDO();
        orgMenuDetp.setOrgMenuType(OrgMenuTypeEnum.MENU);
        initMenuProperty(orgMenuDetp);
        orgMenuDetp.setParentOrgMenuId(superMenu.getOrgMenuId());
        orgMenuDetp.setOrgMenuName("部门管理");
        orgMenuDetp.setOrgInfoId(orgInfoId);
        orgMenuDetp.setOrgMenuPermission("org:dept:list");
        orgMenuDetp.setOrgMenuSort(40);
        this.orgMenuMapper.insert(orgMenuDetp);

        OrgMenuDO orgMenuRole = new OrgMenuDO();
        orgMenuRole.setOrgMenuType(OrgMenuTypeEnum.MENU);
        initMenuProperty(orgMenuRole);
        orgMenuRole.setParentOrgMenuId(superMenu.getOrgMenuId());
        orgMenuRole.setOrgMenuName("角色管理");
        orgMenuRole.setOrgInfoId(orgInfoId);
        orgMenuRole.setOrgMenuPermission("org:role:list");
        orgMenuRole.setOrgMenuSort(60);
        this.orgMenuMapper.insert(orgMenuRole);

        OrgMenuDO orgMenu = new OrgMenuDO();
        orgMenu.setOrgMenuType(OrgMenuTypeEnum.MENU);
        initMenuProperty(orgMenu);
        orgMenu.setParentOrgMenuId(superMenu.getOrgMenuId());
        orgMenu.setOrgMenuName("菜单管理");
        orgMenu.setOrgInfoId(orgInfoId);
        orgMenu.setOrgMenuPermission("org:menu:list");
        orgMenu.setOrgMenuSort(80);
        this.orgMenuMapper.insert(orgMenu);

        OrgMenuDO orgMenuAccount = new OrgMenuDO();
        orgMenuAccount.setOrgMenuType(OrgMenuTypeEnum.MENU);
        initMenuProperty(orgMenuAccount);
        orgMenuAccount.setParentOrgMenuId(superMenu.getOrgMenuId());
        orgMenuAccount.setOrgMenuName("账号管理");
        orgMenuAccount.setOrgInfoId(orgInfoId);
        orgMenuAccount.setOrgMenuPermission("org:user:list");
        orgMenuAccount.setOrgMenuSort(100);
        this.orgMenuMapper.insert(orgMenuAccount);

        OrgMenuDO orgMenuPermission = new OrgMenuDO();
        orgMenuPermission.setOrgMenuType(OrgMenuTypeEnum.MENU);
        initMenuProperty(orgMenuPermission);
        orgMenuPermission.setParentOrgMenuId(superMenu.getOrgMenuId());
        orgMenuPermission.setOrgMenuName("权限管理");
        orgMenuPermission.setOrgInfoId(orgInfoId);
        orgMenuPermission.setOrgMenuPermission("org:permission:list");
        orgMenuPermission.setOrgMenuSort(120);
        this.orgMenuMapper.insert(orgMenuPermission);

        return List.of(orgMenuOrgInfo, orgMenuDetp, orgMenuRole, orgMenuAccount, orgMenuPermission,orgMenu);
    }
    /**
     * 通过父菜单以及机构ID来构建子菜单,并插入数据库 TODO
     * @param lastSubMenu 父菜单
     * @param orgInfoId 机构ID
     * @return 子菜单列表
     */
    private List<OrgMenuDO> initLastSubMenus(OrgMenuDO lastSubMenu, String orgInfoId) {
        String[] permissions = lastSubMenu.getOrgMenuPermission().split(":");

        OrgMenuDO orgMenuCreate = new OrgMenuDO();
        orgMenuCreate.setOrgMenuType(OrgMenuTypeEnum.BUTTON);
        initMenuProperty(orgMenuCreate);
        orgMenuCreate.setParentOrgMenuId(lastSubMenu.getOrgMenuId());
        orgMenuCreate.setOrgMenuName(lastSubMenu.getOrgMenuName()+" 创建");
        orgMenuCreate.setOrgInfoId(orgInfoId);
        orgMenuCreate.setOrgMenuPermission(permissions[0]+":"+permissions[1]+":create");
        orgMenuCreate.setOrgMenuSort(500);
        this.orgMenuMapper.insert(orgMenuCreate);

        OrgMenuDO orgMenuList = new OrgMenuDO();
        orgMenuList.setOrgMenuType(OrgMenuTypeEnum.BUTTON);
        initMenuProperty(orgMenuList);
        orgMenuList.setParentOrgMenuId(lastSubMenu.getOrgMenuId());
        orgMenuList.setOrgMenuName(lastSubMenu.getOrgMenuName()+" 查询");
        orgMenuList.setOrgInfoId(orgInfoId);
        orgMenuList.setOrgMenuPermission(permissions[0]+":"+permissions[1]+":list");
        orgMenuList.setOrgMenuSort(500);
        this.orgMenuMapper.insert(orgMenuList);

        OrgMenuDO orgMenuUpdate = new OrgMenuDO();
        orgMenuUpdate.setOrgMenuType(OrgMenuTypeEnum.BUTTON);
        initMenuProperty(orgMenuUpdate);
        orgMenuUpdate.setParentOrgMenuId(lastSubMenu.getOrgMenuId());
        orgMenuUpdate.setOrgMenuName(lastSubMenu.getOrgMenuName()+" 修改");
        orgMenuUpdate.setOrgInfoId(orgInfoId);
        orgMenuUpdate.setOrgMenuPermission(permissions[0]+":"+permissions[1]+":update");
        orgMenuUpdate.setOrgMenuSort(500);
        this.orgMenuMapper.insert(orgMenuUpdate);

        OrgMenuDO orgMenuDelete = new OrgMenuDO();
        orgMenuDelete.setOrgMenuType(OrgMenuTypeEnum.BUTTON);
        initMenuProperty(orgMenuDelete);
        orgMenuDelete.setParentOrgMenuId(lastSubMenu.getOrgMenuId());
        orgMenuDelete.setOrgMenuName(lastSubMenu.getOrgMenuName()+" 删除");
        orgMenuDelete.setOrgInfoId(orgInfoId);
        orgMenuDelete.setOrgMenuPermission(permissions[0]+":"+permissions[1]+":delete");
        orgMenuDelete.setOrgMenuSort(500);
        this.orgMenuMapper.insert(orgMenuDelete);

        return List.of(orgMenuCreate, orgMenuUpdate, orgMenuList, orgMenuDelete);
    }
}
